package es.udima.geo.geoserver.business;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import es.udima.geo.geoserver.entities.User;
import es.udima.geo.geoserver.entities.UserSession;
import es.udima.geo.geoserver.entities.UserSessionError;
import es.udima.geo.geoserver.entities.UserUpdateResult;
import es.udima.geo.geoserver.entities.UserUpdateResultError;
import es.udima.geo.geoserver.entities.VehicleType;

public class UserController {

    private static final long SESSION_EXPIRING_TIME = 10 * 60 * 1000;

    private static Map<String, User> users = new HashMap<String, User>();
    private static Map<String, User> usersByUsername = new HashMap<String, User>();
    private static Map<String, UserSession> userSessions = new HashMap<String, UserSession>();

    static {
	User u1 = new User();
	u1.setId("uid001");
	u1.setUserName("usuario001");
	u1.setPassword("usuario001");
	u1.setFirstName("David");
	u1.setLastName("Muñoz");
	u1.setVehicleType(VehicleType.STANDAR);
	u1.setFetchGpsLapseTime(2000);
	u1.setLastConnection(new Date());
	users.put(u1.getId(), u1);
	usersByUsername.put(u1.getUserName(), u1);

	User u2 = new User();
	u2.setId("uid002");
	u2.setUserName("usuario002");
	u2.setPassword("usuario002");
	u2.setFirstName("Luís");
	u2.setLastName("Serrano");
	u2.setFetchGpsLapseTime(2000);
	u2.setVehicleType(VehicleType.SPORT);
	users.put(u2.getId(), u2);
	usersByUsername.put(u2.getUserName(), u2);

	User u3 = new User();
	u3.setId("uid003");
	u3.setUserName("usuario003");
	u3.setPassword("usuario003");
	u3.setFirstName("Juan Luís");
	u3.setLastName("Rubio");
	u2.setFetchGpsLapseTime(1000);
	u3.setVehicleType(VehicleType.SUV);
	users.put(u3.getId(), u3);
	usersByUsername.put(u3.getUserName(), u3);
    }

    public UserController() {
    }

    /**
     * 
     * @param userName
     * @return
     */
    public User retrieveUserByUserName(String userName) {
	// TODO retrieveUserByUserName
	return usersByUsername.get(userName);
    }

    /**
     * 
     * @param userId
     * @return
     */
    public User retrieveUserByUserId(String userId) {
	// TODO retrieveUserByUserId
	return users.get(userId);
    }

    /**
     * 
     * @return
     */
    public List<User> retrieveAllUsers() {
	List<User> res = new ArrayList<User>();
	res.addAll(users.values());
	for (User u : res) {
	    if (userSessions.get(u.getId()) != null) {
		// TODO control the session expiry time
		u.setConnected(true);
	    } else {
		u.setConnected(false);
	    }
	}
	return res;
    }

    /**
     * 
     * @param user
     * @return
     */
    public UserUpdateResult createUser(User user) {
	// TODO createUser
	UserUpdateResult res = new UserUpdateResult();
	
	// TODO validate user

	//assign new user id
	user.setId(generateUserId());

	// TODO save user
	users.put(user.getId(), user);

	res.setUser(user);
	return res;
    }

    /**
     * 
     * @param user
     * @return
     */
    public UserUpdateResult updateUser(User user) {
	// TODO updateUser
	UserUpdateResult res = new UserUpdateResult();
	res.setUser(user);
	return res;
    }

    /**
     * 
     * @param userId
     * @param oldPassword
     * @param newPassword1
     * @param newPassword2
     * @return
     */
    public UserUpdateResult updateUserPassword(String userId, String oldPassword, String newPassword1, String newPassword2) {
	// TODO updateUserPassword
	UserUpdateResult res = new UserUpdateResult();
	User user = retrieveUserByUserId(userId);
	if (user == null) {
	    res.addError(UserUpdateResultError.USER_NOT_EXISTS);
	}

	if (oldPassword.isEmpty()) {
	    res.addError(UserUpdateResultError.OLDPASSWORD_BLANK);
	} else if (user != null && !user.getPassword().equals(oldPassword)) {
	    res.addError(UserUpdateResultError.OLDPASSWORD_INCORRECT);
	}

	if (newPassword1.isEmpty() && newPassword2.isEmpty()) {
	    res.addError(UserUpdateResultError.NEWPASSWORD_BLANK);
	} else if ((!newPassword1.isEmpty() && newPassword2.isEmpty()) || (!newPassword2.isEmpty() && newPassword1.isEmpty())
		|| (!newPassword1.isEmpty() && !newPassword2.isEmpty() && !newPassword1.equals(newPassword2))) {
	    res.addError(UserUpdateResultError.NEWPASSWORDS_UNMATCHED);
	} else if (!oldPassword.equals(newPassword1)) {
	    res.addError(UserUpdateResultError.NEWPASSWORD_REPEATED);
	} else if (!validatePasswordAppropriateness(user, newPassword1)) {
	    res.addError(UserUpdateResultError.NEWPASSWORD_NOT_COMPLIANT);
	}

	if (res.isUpdateSuccessful()) {
	    // TODO change password
	    user.setPassword(newPassword1);
	}

	return res;
    }

    /**
     * 
     * @param user
     * @return
     */
    public UserSession connectUser(User user) {
	// TODO connectUser
	user.setConnected(true);
	user.setLastConnection(new Date());
	UserSession us = new UserSession();
	us.setUserId(user.getId());
	us.setSessionToken(generateSessionToken());
	us.setLastTimeUsed(new Date());
	userSessions.put(us.getUserId(), us);
	return us;
    }

    /**
     * 
     * @param userSession
     */
    public void disconnectUser(UserSession userSession) {
	// TODO disconnectUser
	UserSession us = userSessions.get(userSession.getUserId());
	if (us != null && userSession.getSessionToken().equals(us.getSessionToken())) {
	    userSessions.remove(us.getUserId());
	}
    }

    /**
     * 
     * @param session
     * @return
     */
    public Collection<UserSessionError> consumeUserSession(UserSession session) {
	Collection<UserSessionError> res = new ArrayList<UserSessionError>();
	UserSession sessionRead = userSessions.get(session.getUserId());
	if (sessionRead == null) {
	    res.add(UserSessionError.SESSION_NOT_EXISTS);
	} else {
	    Date lastUse = sessionRead.getLastTimeUsed();
	    Date now = new Date();
	    if (lastUse.getTime() + SESSION_EXPIRING_TIME < now.getTime()) {
		res.add(UserSessionError.SESSION_EXPIRED);
		disconnectUser(session);
	    } else {
		sessionRead.setLastTimeUsed(now);
	    }
	}
	return res;
    }

    /*----------------------------------------------------------------------------------------*/

    /**
     * 
     * @return
     */
    private String generateSessionToken() {
	// TODO implements a secure session token generator
	SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
	Date now = new Date();
	return sdf.format(now);
    }

    /**
     * 
     * @param userSession
     * @return
     */
    private boolean calculateUserSessionHasExpired(UserSession userSession) {
	// TODO calculate session expiration
	return false;
    }

    /**
     * 
     * @param user
     * @param password
     * @return
     */
    private boolean validatePasswordAppropriateness(User user, String password) {
	// TODO validatePasswordAppropriateness
	return true;
    }

    private String generateUserId() {
	SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
	Date now = new Date();
	return sdf.format(now);
    }
}
